In this HTML document, we explored the Gapminder dataset and generated the following analyses and report:

Preparation

suppressPackageStartupMessages({
  library(tidyverse)
  library(plotly)
  library(multcomp)
  library(DT)
})

We first read in the data

gapminder_clean <- read_csv("gapminder_clean.csv", show_col_types = FALSE)

These are the column names to the data table:

colnames(dplyr::select(gapminder_clean, -"...1"))
 [1] "Country Name"                                           
 [2] "Year"                                                   
 [3] "Agriculture, value added (% of GDP)"                    
 [4] "CO2 emissions (metric tons per capita)"                 
 [5] "Domestic credit provided by financial sector (% of GDP)"
 [6] "Electric power consumption (kWh per capita)"            
 [7] "Energy use (kg of oil equivalent per capita)"           
 [8] "Exports of goods and services (% of GDP)"               
 [9] "Fertility rate, total (births per woman)"               
[10] "GDP growth (annual %)"                                  
[11] "Imports of goods and services (% of GDP)"               
[12] "Industry, value added (% of GDP)"                       
[13] "Inflation, GDP deflator (annual %)"                     
[14] "Life expectancy at birth, total (years)"                
[15] "Population density (people per sq. km of land area)"    
[16] "Services, etc., value added (% of GDP)"                 
[17] "pop"                                                    
[18] "continent"                                              
[19] "gdpPercap"                                              

CO2 Emissions and GDP Per Capita

Year 1962

Instructions: Filter the data to include only rows where Year is 1962 and then make a scatter plot comparing CO2 emissions (metric tons per capita) and gdpPercap for the filtered data.

We started by filtering the dataset to Year == 1962:

gapminder_clean.filtered <- gapminder_clean %>% 
  filter(Year == 1962)

The following is a scatter plot of CO2 emissions-gdpPercap in year 1962, with x = CO2 emissions, and y = gdpPercap. By observation, the data fits a linear model, so we also plotted it on the diagram:

plot <- gapminder_clean.filtered %>% 
  ggplot(aes(x = `CO2 emissions (metric tons per capita)`, 
             y = gdpPercap)) + 
  geom_point(na.rm = TRUE) + 
  scale_x_log10() + 
  scale_y_log10() + 
  geom_smooth(method = "lm", na.rm = TRUE) + 
  ggtitle("CO2 Emissions-GDP Per Capital in Year 1962")
ggplotly(plot, height = 350, width = 600)

Instructions: On the filtered data, calculate the correlation of CO2 emissions (metric tons per capita) and gdpPercap. What is the correlation and associated p value?

To determine the Pearson correlation coefficient, we should a priori ensure the data is normally distributed. This is implemented by performing the Shapiro-Wilk normality test on our x’s and y’s. (A p-value < 0.05 indicates that our data fit a normal distribution).

shapiro.test(gapminder_clean.filtered$`CO2 emissions (metric tons per capita)`)

    Shapiro-Wilk normality test

data:  gapminder_clean.filtered$`CO2 emissions (metric tons per capita)`
W = 0.45714, p-value < 2.2e-16
shapiro.test(gapminder_clean.filtered$gdpPercap)

    Shapiro-Wilk normality test

data:  gapminder_clean.filtered$gdpPercap
W = 0.38638, p-value < 2.2e-16

Both x’s and y’s fit a normal distribution. Thus, a Pearson correlation coefficient could be calculated. Otherwise, a rank-based correlation test such as the Spearman correlation test should be used.

cor <- cor.test(
  x = gapminder_clean.filtered$`CO2 emissions (metric tons per capita)`,
  y = gapminder_clean.filtered$gdpPercap, 
  method="pearson", 
  use = "complete.obs")
cor

    Pearson's product-moment correlation

data:  gapminder_clean.filtered$`CO2 emissions (metric tons per capita)` and gapminder_clean.filtered$gdpPercap
t = 25.269, df = 106, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8934697 0.9489792
sample estimates:
      cor 
0.9260817 

Findings: The CO2 emissions (metric tons per capita) and gdpPercap are positively correlated. The Pearson correlation coefficient is 0.9260817, which means that as GDP per capital rises, so does CO2 emission, and vice versa. The p-value is 1.1286792^{-46}, which negates the null hypothesis that the relationship is a result of pure chance.

Year of the strongest correlation

Instructions: On the unfiltered data, answer “In what year is the correlation between 'CO2 emissions (metric tons per capita)' and gdpPercap the strongest?” Filter the dataset to that year for the next step...

We grouped the dataset by year, calculating the Pearson correlation coefficient between CO2 emissions and gdpPercap of each. The results were visualized by a line plot.

gapminder_clean.cor_by_year <- gapminder_clean %>% 
  group_by(Year) %>% 
  summarize(cor = cor(`CO2 emissions (metric tons per capita)`, gdpPercap, use = "complete.obs")) %>% 
  arrange(desc(cor))
plot <- gapminder_clean.cor_by_year %>% 
  ggplot(aes(x = Year, y = cor)) + 
  geom_line() + 
  geom_point()
ggplotly(plot, height = 350, width = 600)

Findings: Year 1967 witnessed the strongest correlation of CO2 emissions and gdpPercap

We then filtered the dataset accordingly:

gapminder_clean.filtered <- gapminder_clean %>% 
  filter(Year== gapminder_clean.cor_by_year[[1,1]])

Instructions: Using plotly, create an interactive scatter plot comparing 'CO2 emissions (metric tons per capita)' and gdpPercap, where the point size is determined by pop (population) and the color is determined by the continent. You can easily convert any ggplot plot to a plotly plot using the ggplotly() command.

The following are the desired plots:

plot <- gapminder_clean.filtered %>%
  ggplot(aes(x = `CO2 emissions (metric tons per capita)`, y = gdpPercap)) +
  geom_point(aes(color = continent, size = pop, text = paste0("country: ", `Country Name`)), na.rm = TRUE) +
  scale_x_log10() +
  scale_y_log10() + 
  ggtitle(paste0("CO2 Emissions-GDP Per Capital in Year ", gapminder_clean.cor_by_year[[1,1]]))
ggplotly(plot, height = 350, width = 600)

Continent and Energy Use

Instructions: What is the relationship between continent and 'Energy use (kg of oil equivalent per capita)'? (stats test needed)

We visualized the data by plotting continent against Energy use on a boxplot:

plot <- gapminder_clean %>% 
  subset(!is.na(continent)) %>% 
  ggplot(aes(x = continent, y = `Energy use (kg of oil equivalent per capita)`, fill = continent)) +
  geom_boxplot(na.rm = TRUE) + 
  ggtitle("Contient-Energy Use")
ggplotly(plot, height = 350, width = 600)

Since we are manipulating over quantitative data (Energy use) among multiple groups (continent), it is worth exploring whether the outcome variable (Energy use) differ among these groups or not. There are more than 2 groups being compared, with only one outcome variable. Thus, an ANOVA is suitable for our case.

We perform an ANOVA with the following code:

res_aov <- aov(`Energy use (kg of oil equivalent per capita)` ~ continent, data = gapminder_clean)
summary(res_aov)
             Df    Sum Sq   Mean Sq F value Pr(>F)    
continent     4 7.715e+08 192870621   51.46 <2e-16 ***
Residuals   843 3.160e+09   3748033                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
1759 observations deleted due to missingness

The ANOVA gave a p-value of 8.5270035^{-39}, which sucessfully negated the null hypothesis that there is no difference among group means.

To find out exactly which groups are different from others, it’s useful to conduct a post hoc test. A Tukey HSD test is commonly used to compare all groups to each other (i.e. all possible comparisons of 2 groups), sparing the need to set a reference group.

res_tukey <- TukeyHSD(res_aov, conf.level = 0.95)
res_tukey
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = `Energy use (kg of oil equivalent per capita)` ~ continent, data = gapminder_clean)

$continent
                      diff       lwr       upr     p adj
Americas-Africa  1005.1037  466.8326 1543.3748 0.0000041
Asia-Africa      1168.7636  628.2529 1709.2742 0.0000000
Europe-Africa    2447.5453 1947.3838 2947.7067 0.0000000
Oceania-Africa   3281.7976 2040.3410 4523.2543 0.0000000
Asia-Americas     163.6599 -384.4160  711.7357 0.9256447
Europe-Americas  1442.4416  934.1141 1950.7691 0.0000000
Oceania-Americas 2276.6940 1031.9249 3521.4630 0.0000069
Europe-Asia      1278.7817  768.0833 1789.4801 0.0000000
Oceania-Asia     2113.0341  867.2950 3358.7732 0.0000402
Oceania-Europe    834.2524 -394.5176 2063.0223 0.3421942
par(mar = c(5.1, 8.1, 4.1, 0.1)) # set margins of plot
plot(res_tukey,
     las = 1)  # labels horizontal to the axes

The plot above depicts the 95% confidence interval (95% CI) of the differences in mean levels of continents. According to the plot, the 95% CI of Asia-Americas and Oceania-Europe overlapped with 0, meaning that we couldn’t effectively tell that these groups have differences between the former and the latter country. All other groups had significant differences.

These are the statistical values from the Tukey HSD test:

data_frame(Group = row.names(res_tukey$continent), 
           as_data_frame(res_tukey$continent)) %>% 
  arrange(desc(`p adj`)) %>% 
  mutate(across(where(is.numeric), round, 4)) %>% 
  datatable()

Findings: There is significant difference between the group means of 'Energy use (kg of oil equivalent per capita)' across the continents. However, a pairwise post-hoc test reveals that it’s not the case between Asia-Americas and Oceania-Europe.

European and Asian Imports of Goods and Services

Instructions: Is there a significant difference between Europe and Asia with respect to 'Imports of goods and services (% of GDP)' in the years after 1990? (stats test needed)

We filtered the dataset according to the instructions and drew a boxplot:

filtered.gapminder_clean <- gapminder_clean %>% 
  filter(continent %in% c("Europe", "Asia"), Year >= 1990)
plot <- filtered.gapminder_clean %>% 
  ggplot(aes(x = continent, y = `Imports of goods and services (% of GDP)`, fill = continent)) +
  geom_boxplot(na.rm = TRUE) + 
  ggtitle("European and Asian Imports of Goods and Services")
ggplotly(plot, height = 350, width = 600)

As can be seen in the boxplot and the following table, Asia has a slightly higher mean of Imports of goods and services compared to Europe.

filtered.gapminder_clean %>% group_by(continent) %>% 
  summarize(mean = mean(`Imports of goods and services (% of GDP)`, na.rm = TRUE), 
            IQR = IQR(`Imports of goods and services (% of GDP)`, na.rm = TRUE)) %>% 
  mutate(across(where(is.numeric), round, 4)) %>% 
  datatable()

Since we are comparing two groups of quantitative data, a two-sample t-test could be useful. This test holds the null hypothesis that the mean of Imports of goods and services does not differ between the two groups.

res_t <- t.test(formula = `Imports of goods and services (% of GDP)` ~ continent, 
                data = filtered.gapminder_clean)
res_t

    Welch Two Sample t-test

data:  Imports of goods and services (% of GDP) by continent
t = 1.3552, df = 137.53, p-value = 0.1776
alternative hypothesis: true difference in means between group Asia and group Europe is not equal to 0
95 percent confidence interval:
 -2.321099 12.433240
sample estimates:
  mean in group Asia mean in group Europe 
            46.84531             41.78924 

Findings: The two-sample t-test gives a p-value of 0.1775691, which is >0.05, meaning that the mean of Imports of goods and services is not statistically different between Europe and Asia.

Countries of Highest Population Density

Instructions: What is the country (or countries) that has the highest 'Population density (people per sq. km of land area)' across all years? (i.e., which country has the highest average ranking in this category across each time point in the dataset?)

To answer this question, the mean of population density over the years is calculated for each country. In the following table, the countries are ordered by the calculated mean (rounded to the nearsert integer), in descending order.

countries_of_highest_density <- gapminder_clean %>% 
  dplyr::select(Year, `Country Name`, `Population density (people per sq. km of land area)`) %>% 
  spread(key = Year, value = `Population density (people per sq. km of land area)`) %>% 
  bind_cols(., Mean = rowMeans(dplyr::select(., -`Country Name`))) %>% 
  arrange(desc(Mean)) %>% 
  relocate(Mean, .after = `Country Name`)
countries_of_highest_density %>% mutate(across(where(is.numeric), round, 0)) %>% datatable(options = list(scrollX = TRUE))

From the table above, Macao SAR, China, Monaco, Hong Kong SAR, China, Singapore, Gibraltar, Bermuda have the top six highest average population density. We could further draw a line plot to observe the changes of population density in these countries over years.

plot <- gapminder_clean %>% 
  filter(`Country Name` %in% head(countries_of_highest_density)$`Country Name`) %>% 
  ggplot(aes(x = Year, y = `Population density (people per sq. km of land area)`, color = `Country Name`)) +
  geom_line(na.rm = TRUE) + 
  geom_point(na.rm = TRUE) + 
  theme(legend.position = "none") + 
  ggtitle("Changes of Population Density in Top Six Countries")
ggplotly(plot, height = 350, width = 600)

Findings: Macao SAR, China, Monaco are the top 2 in average Population density (people per sq. km of land area) across all years.

Countries of Greatest Life Expectancy Increase

Instructions: What country (or countries) has shown the greatest increase in ‘Life expectancy at birth, total (years)’ between 1962 and 2007?

To answer the question, we first transform the table into Country name x Year, and subtract the whole table by value of Life expectancy at birth, total (years) in 1962.

selected_col.gapminder_clean <- gapminder_clean %>%
  dplyr::select(Year, `Country Name`, `Life expectancy at birth, total (years)`) %>%
  spread(key = `Year`, value = `Life expectancy at birth, total (years)`)

selected_col.gapminder_clean <- data_frame(
  `Country Name` = selected_col.gapminder_clean$`Country Name`,
  dplyr::select(selected_col.gapminder_clean, -`Country Name`) - selected_col.gapminder_clean$`1962` # Subtract the whole table by value in 1962
) %>%
  gather(-`Country Name`, key = Year, value = `Life expectancy at birth, total (years)`)

The table below shows the difference of Life expectancy at birth, total (years) between 2007 and 1962, arranged in descending order, of each country:

countries_of_greatest_life_expectancy_increase <- selected_col.gapminder_clean %>% 
  dplyr::select(Year, `Country Name`, `Life expectancy at birth, total (years)`) %>% 
  spread(key = `Year`, value = `Life expectancy at birth, total (years)`) %>% 
  dplyr::select(`Country Name`, `Difference between 2007 and 1962` = `2007`) %>% 
  mutate(across(where(is.numeric), round, 4)) %>% 
  arrange(desc(`Difference between 2007 and 1962`))
countries_of_greatest_life_expectancy_increase %>% datatable()

The line plot shows the top six countries regarding life expectancy growth. Year 1962 of each country is set to a baseline 0 for better comparison.

plot <- selected_col.gapminder_clean %>% 
  subset(`Country Name` %in% head(countries_of_greatest_life_expectancy_increase)$`Country Name`) %>% 
  ggplot(aes(x = Year, y = `Life expectancy at birth, total (years)`, color = `Country Name`)) +
  geom_line(aes(group = `Country Name`), na.rm = TRUE) + 
  geom_point(na.rm = TRUE) + 
  labs(y = "Difference of life expectancy at birth, total (years)") +
  theme(legend.position = "none") + 
  ggtitle("Changes of Life Expectancy Growth in Top Six Countries")
ggplotly(plot, height = 350, width = 600)

Findings: Maldives has shown the greatest increase in Life expectancy at birth, total (years) between 1962 and 2007.

Session Info

sessionInfo()
R version 4.2.0 (2022-04-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS 13.2.1

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] DT_0.27         multcomp_1.4-23 TH.data_1.1-1   MASS_7.3-58.3   survival_3.5-5 
 [6] mvtnorm_1.1-3   plotly_4.10.1   lubridate_1.9.2 forcats_1.0.0   stringr_1.5.0  
[11] dplyr_1.1.0     purrr_1.0.1     readr_2.1.4     tidyr_1.3.0     tibble_3.2.0   
[16] ggplot2_3.4.1   tidyverse_2.0.0

loaded via a namespace (and not attached):
 [1] lattice_0.20-45   zoo_1.8-11        digest_0.6.31     utf8_1.2.3       
 [5] R6_2.5.1          evaluate_0.20     httr_1.4.5        pillar_1.8.1     
 [9] rlang_1.1.0       lazyeval_0.2.2    rstudioapi_0.14   data.table_1.14.8
[13] jquerylib_0.1.4   Matrix_1.5-3      rmarkdown_2.20    labeling_0.4.2   
[17] splines_4.2.0     htmlwidgets_1.6.2 bit_4.0.5         munsell_0.5.0    
[21] compiler_4.2.0    xfun_0.37         pkgconfig_2.0.3   mgcv_1.8-42      
[25] htmltools_0.5.4   tidyselect_1.2.0  codetools_0.2-19  fansi_1.0.4      
[29] viridisLite_0.4.1 crayon_1.5.2      tzdb_0.3.0        withr_2.5.0      
[33] grid_4.2.0        nlme_3.1-162      jsonlite_1.8.4    gtable_0.3.2     
[37] lifecycle_1.0.3   magrittr_2.0.3    scales_1.2.1      cli_3.6.0        
[41] stringi_1.7.12    vroom_1.6.1       cachem_1.0.7      farver_2.1.1     
[45] bslib_0.4.2       ellipsis_0.3.2    generics_0.1.3    vctrs_0.6.0      
[49] sandwich_3.0-2    tools_4.2.0       bit64_4.0.5       glue_1.6.2       
[53] crosstalk_1.2.0   hms_1.1.2         rsconnect_0.8.29  parallel_4.2.0   
[57] fastmap_1.1.1     yaml_2.3.7        timechange_0.2.0  colorspace_2.1-0 
[61] knitr_1.42        sass_0.4.5       
LS0tCnRpdGxlOiAiUiBmb3IgRGF0YSBTY2llbmNlIFNraWxscyBBc3Nlc3NtZW50IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KCj4gSW4gdGhpcyBIVE1MIGRvY3VtZW50LCB3ZSBleHBsb3JlZCB0aGUgKkdhcG1pbmRlciogZGF0YXNldCBhbmQgZ2VuZXJhdGVkIHRoZSBmb2xsb3dpbmcgYW5hbHlzZXMgYW5kIHJlcG9ydDoKCiMjIFByZXBhcmF0aW9uCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KHBsb3RseSkKICBsaWJyYXJ5KG11bHRjb21wKQogIGxpYnJhcnkoRFQpCn0pCmBgYAoKV2UgZmlyc3QgcmVhZCBpbiB0aGUgZGF0YQoKYGBge3J9CmdhcG1pbmRlcl9jbGVhbiA8LSByZWFkX2NzdigiZ2FwbWluZGVyX2NsZWFuLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCmBgYAoKVGhlc2UgYXJlIHRoZSBjb2x1bW4gbmFtZXMgdG8gdGhlIGRhdGEgdGFibGU6CgpgYGB7cn0KY29sbmFtZXMoZHBseXI6OnNlbGVjdChnYXBtaW5kZXJfY2xlYW4sIC0iLi4uMSIpKQpgYGAKCiMjIENPMiBFbWlzc2lvbnMgYW5kIEdEUCBQZXIgQ2FwaXRhCgojIyMgWWVhciAxOTYyCgo+IEluc3RydWN0aW9uczogRmlsdGVyIHRoZSBkYXRhIHRvIGluY2x1ZGUgb25seSByb3dzIHdoZXJlIGBZZWFyYCBpcyBgMTk2MmAgYW5kIHRoZW4gbWFrZSBhIHNjYXR0ZXIgcGxvdCBjb21wYXJpbmcgYENPMiBlbWlzc2lvbnMgKG1ldHJpYyB0b25zIHBlciBjYXBpdGEpYCBhbmQgYGdkcFBlcmNhcGAgZm9yIHRoZSBmaWx0ZXJlZCBkYXRhLgoKV2Ugc3RhcnRlZCBieSBmaWx0ZXJpbmcgdGhlIGRhdGFzZXQgdG8gYFllYXIgPT0gMTk2MmA6CgpgYGB7cn0KZ2FwbWluZGVyX2NsZWFuLmZpbHRlcmVkIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgZmlsdGVyKFllYXIgPT0gMTk2MikKYGBgCgpUaGUgZm9sbG93aW5nIGlzIGEgc2NhdHRlciBwbG90IG9mIENPMiBlbWlzc2lvbnMtZ2RwUGVyY2FwIGluIHllYXIgMTk2Miwgd2l0aCB4ID0gQ08yIGVtaXNzaW9ucywgYW5kIHkgPSBnZHBQZXJjYXAuIEJ5IG9ic2VydmF0aW9uLCB0aGUgZGF0YSBmaXRzIGEgbGluZWFyIG1vZGVsLCBzbyB3ZSBhbHNvIHBsb3R0ZWQgaXQgb24gdGhlIGRpYWdyYW06CgpgYGB7cn0KcGxvdCA8LSBnYXBtaW5kZXJfY2xlYW4uZmlsdGVyZWQgJT4lIAogIGdncGxvdChhZXMoeCA9IGBDTzIgZW1pc3Npb25zIChtZXRyaWMgdG9ucyBwZXIgY2FwaXRhKWAsIAogICAgICAgICAgICAgeSA9IGdkcFBlcmNhcCkpICsgCiAgZ2VvbV9wb2ludChuYS5ybSA9IFRSVUUpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgbmEucm0gPSBUUlVFKSArIAogIGdndGl0bGUoIkNPMiBFbWlzc2lvbnMtR0RQIFBlciBDYXBpdGFsIGluIFllYXIgMTk2MiIpCmdncGxvdGx5KHBsb3QsIGhlaWdodCA9IDM1MCwgd2lkdGggPSA2MDApCmBgYAoKPiBJbnN0cnVjdGlvbnM6IE9uIHRoZSBmaWx0ZXJlZCBkYXRhLCBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIG9mIGBDTzIgZW1pc3Npb25zIChtZXRyaWMgdG9ucyBwZXIgY2FwaXRhKWAgYW5kIGBnZHBQZXJjYXBgLiBXaGF0IGlzIHRoZSBjb3JyZWxhdGlvbiBhbmQgYXNzb2NpYXRlZCBwIHZhbHVlPwoKVG8gZGV0ZXJtaW5lIHRoZSAqUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCosIHdlIHNob3VsZCBhIHByaW9yaSBlbnN1cmUgdGhlIGRhdGEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFRoaXMgaXMgaW1wbGVtZW50ZWQgYnkgcGVyZm9ybWluZyB0aGUgKlNoYXBpcm8tV2lsayBub3JtYWxpdHkgdGVzdCogb24gb3VyIHgncyBhbmQgeSdzLiAoQSBwLXZhbHVlIFw8IDAuMDUgaW5kaWNhdGVzIHRoYXQgb3VyIGRhdGEgZml0IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbikuCgpgYGB7cn0Kc2hhcGlyby50ZXN0KGdhcG1pbmRlcl9jbGVhbi5maWx0ZXJlZCRgQ08yIGVtaXNzaW9ucyAobWV0cmljIHRvbnMgcGVyIGNhcGl0YSlgKQpzaGFwaXJvLnRlc3QoZ2FwbWluZGVyX2NsZWFuLmZpbHRlcmVkJGdkcFBlcmNhcCkKYGBgCgpCb3RoIHgncyBhbmQgeSdzIGZpdCBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRodXMsIGEgKlBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQqIGNvdWxkIGJlIGNhbGN1bGF0ZWQuIE90aGVyd2lzZSwgYSByYW5rLWJhc2VkIGNvcnJlbGF0aW9uIHRlc3Qgc3VjaCBhcyB0aGUgU3BlYXJtYW4gY29ycmVsYXRpb24gdGVzdCBzaG91bGQgYmUgdXNlZC4KCmBgYHtyfQpjb3IgPC0gY29yLnRlc3QoCiAgeCA9IGdhcG1pbmRlcl9jbGVhbi5maWx0ZXJlZCRgQ08yIGVtaXNzaW9ucyAobWV0cmljIHRvbnMgcGVyIGNhcGl0YSlgLAogIHkgPSBnYXBtaW5kZXJfY2xlYW4uZmlsdGVyZWQkZ2RwUGVyY2FwLCAKICBtZXRob2Q9InBlYXJzb24iLCAKICB1c2UgPSAiY29tcGxldGUub2JzIikKY29yCmBgYAoKPiBGaW5kaW5nczogVGhlIGBDTzIgZW1pc3Npb25zIChtZXRyaWMgdG9ucyBwZXIgY2FwaXRhKWAgYW5kIGBnZHBQZXJjYXBgIGFyZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQuIFRoZSAqUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCogaXMgYHIgY29yJGVzdGltYXRlYCwgd2hpY2ggbWVhbnMgdGhhdCBhcyBHRFAgcGVyIGNhcGl0YWwgcmlzZXMsIHNvIGRvZXMgQ08yIGVtaXNzaW9uLCBhbmQgdmljZSB2ZXJzYS4gVGhlIHAtdmFsdWUgaXMgYHIgY29yJHAudmFsdWVgLCB3aGljaCBuZWdhdGVzIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGlzIGEgcmVzdWx0IG9mIHB1cmUgY2hhbmNlLgoKIyMjIFllYXIgb2YgdGhlIHN0cm9uZ2VzdCBjb3JyZWxhdGlvbgoKPiBJbnN0cnVjdGlvbnM6IE9uIHRoZSB1bmZpbHRlcmVkIGRhdGEsIGFuc3dlciAiSW4gd2hhdCB5ZWFyIGlzIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGAnQ08yIGVtaXNzaW9ucyAobWV0cmljIHRvbnMgcGVyIGNhcGl0YSknYCBhbmQgYGdkcFBlcmNhcGAgdGhlIHN0cm9uZ2VzdD8iIEZpbHRlciB0aGUgZGF0YXNldCB0byB0aGF0IHllYXIgZm9yIHRoZSBuZXh0IHN0ZXBcLi4uCgpXZSBncm91cGVkIHRoZSBkYXRhc2V0IGJ5IHllYXIsIGNhbGN1bGF0aW5nIHRoZSAqUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCogYmV0d2VlbiBgQ08yIGVtaXNzaW9uc2AgYW5kIGBnZHBQZXJjYXBgIG9mIGVhY2guIFRoZSByZXN1bHRzIHdlcmUgdmlzdWFsaXplZCBieSBhIGxpbmUgcGxvdC4KCmBgYHtyfQpnYXBtaW5kZXJfY2xlYW4uY29yX2J5X3llYXIgPC0gZ2FwbWluZGVyX2NsZWFuICU+JSAKICBncm91cF9ieShZZWFyKSAlPiUgCiAgc3VtbWFyaXplKGNvciA9IGNvcihgQ08yIGVtaXNzaW9ucyAobWV0cmljIHRvbnMgcGVyIGNhcGl0YSlgLCBnZHBQZXJjYXAsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhjb3IpKQpwbG90IDwtIGdhcG1pbmRlcl9jbGVhbi5jb3JfYnlfeWVhciAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IGNvcikpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX3BvaW50KCkKZ2dwbG90bHkocGxvdCwgaGVpZ2h0ID0gMzUwLCB3aWR0aCA9IDYwMCkKYGBgCgo+IEZpbmRpbmdzOiBZZWFyIGByIGdhcG1pbmRlcl9jbGVhbi5jb3JfYnlfeWVhcltbMSwxXV1gIHdpdG5lc3NlZCB0aGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uIG9mIGBDTzIgZW1pc3Npb25zYCBhbmQgYGdkcFBlcmNhcGAKCldlIHRoZW4gZmlsdGVyZWQgdGhlIGRhdGFzZXQgYWNjb3JkaW5nbHk6CgpgYGB7cn0KZ2FwbWluZGVyX2NsZWFuLmZpbHRlcmVkIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgZmlsdGVyKFllYXI9PSBnYXBtaW5kZXJfY2xlYW4uY29yX2J5X3llYXJbWzEsMV1dKQpgYGAKCj4gSW5zdHJ1Y3Rpb25zOiBVc2luZyBgcGxvdGx5YCwgY3JlYXRlIGFuIGludGVyYWN0aXZlIHNjYXR0ZXIgcGxvdCBjb21wYXJpbmcgYCdDTzIgZW1pc3Npb25zIChtZXRyaWMgdG9ucyBwZXIgY2FwaXRhKSdgIGFuZCBgZ2RwUGVyY2FwYCwgd2hlcmUgdGhlIHBvaW50IHNpemUgaXMgZGV0ZXJtaW5lZCBieSBgcG9wYCAocG9wdWxhdGlvbikgYW5kIHRoZSBjb2xvciBpcyBkZXRlcm1pbmVkIGJ5IHRoZSBgY29udGluZW50YC4gWW91IGNhbiBlYXNpbHkgY29udmVydCBhbnkgYGdncGxvdGAgcGxvdCB0byBhIGBwbG90bHlgIHBsb3QgdXNpbmcgdGhlIGBnZ3Bsb3RseSgpYCBjb21tYW5kLgoKVGhlIGZvbGxvd2luZyBhcmUgdGhlIGRlc2lyZWQgcGxvdHM6CgpgYGB7cn0KcGxvdCA8LSBnYXBtaW5kZXJfY2xlYW4uZmlsdGVyZWQgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYENPMiBlbWlzc2lvbnMgKG1ldHJpYyB0b25zIHBlciBjYXBpdGEpYCwgeSA9IGdkcFBlcmNhcCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNvbnRpbmVudCwgc2l6ZSA9IHBvcCwgdGV4dCA9IHBhc3RlMCgiY291bnRyeTogIiwgYENvdW50cnkgTmFtZWApKSwgbmEucm0gPSBUUlVFKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKyAKICBnZ3RpdGxlKHBhc3RlMCgiQ08yIEVtaXNzaW9ucy1HRFAgUGVyIENhcGl0YWwgaW4gWWVhciAiLCBnYXBtaW5kZXJfY2xlYW4uY29yX2J5X3llYXJbWzEsMV1dKSkKZ2dwbG90bHkocGxvdCwgaGVpZ2h0ID0gMzUwLCB3aWR0aCA9IDYwMCkKYGBgCgojIyBDb250aW5lbnQgYW5kIEVuZXJneSBVc2UKCj4gSW5zdHJ1Y3Rpb25zOiBXaGF0IGlzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgY29udGluZW50YCBhbmQgYCdFbmVyZ3kgdXNlIChrZyBvZiBvaWwgZXF1aXZhbGVudCBwZXIgY2FwaXRhKSdgPyAoc3RhdHMgdGVzdCBuZWVkZWQpCgpXZSB2aXN1YWxpemVkIHRoZSBkYXRhIGJ5IHBsb3R0aW5nIGBjb250aW5lbnRgIGFnYWluc3QgYEVuZXJneSB1c2VgIG9uIGEgYm94cGxvdDoKCmBgYHtyfQpwbG90IDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgc3Vic2V0KCFpcy5uYShjb250aW5lbnQpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gY29udGluZW50LCB5ID0gYEVuZXJneSB1c2UgKGtnIG9mIG9pbCBlcXVpdmFsZW50IHBlciBjYXBpdGEpYCwgZmlsbCA9IGNvbnRpbmVudCkpICsKICBnZW9tX2JveHBsb3QobmEucm0gPSBUUlVFKSArIAogIGdndGl0bGUoIkNvbnRpZW50LUVuZXJneSBVc2UiKQpnZ3Bsb3RseShwbG90LCBoZWlnaHQgPSAzNTAsIHdpZHRoID0gNjAwKQpgYGAKClNpbmNlIHdlIGFyZSBtYW5pcHVsYXRpbmcgb3ZlciBxdWFudGl0YXRpdmUgZGF0YSAoYEVuZXJneSB1c2VgKSBhbW9uZyBtdWx0aXBsZSBncm91cHMgKGBjb250aW5lbnRgKSwgaXQgaXMgd29ydGggZXhwbG9yaW5nIHdoZXRoZXIgdGhlIG91dGNvbWUgdmFyaWFibGUgKGBFbmVyZ3kgdXNlYCkgZGlmZmVyIGFtb25nIHRoZXNlIGdyb3VwcyBvciBub3QuIFRoZXJlIGFyZSBtb3JlIHRoYW4gMiBncm91cHMgYmVpbmcgY29tcGFyZWQsIHdpdGggb25seSBvbmUgb3V0Y29tZSB2YXJpYWJsZS4gVGh1cywgYW4gKkFOT1ZBKiBpcyBzdWl0YWJsZSBmb3Igb3VyIGNhc2UuCgpXZSBwZXJmb3JtIGFuICpBTk9WQSogd2l0aCB0aGUgZm9sbG93aW5nIGNvZGU6CgpgYGB7cn0KcmVzX2FvdiA8LSBhb3YoYEVuZXJneSB1c2UgKGtnIG9mIG9pbCBlcXVpdmFsZW50IHBlciBjYXBpdGEpYCB+IGNvbnRpbmVudCwgZGF0YSA9IGdhcG1pbmRlcl9jbGVhbikKc3VtbWFyeShyZXNfYW92KQpgYGAKClRoZSAqQU5PVkEqIGdhdmUgYSBwLXZhbHVlIG9mIGByIHN1bW1hcnkocmVzX2FvdilbWzFdXVsxLCA1XWAsIHdoaWNoIHN1Y2Vzc2Z1bGx5IG5lZ2F0ZWQgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0IHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgYW1vbmcgZ3JvdXAgbWVhbnMuCgpUbyBmaW5kIG91dCBleGFjdGx5IHdoaWNoIGdyb3VwcyBhcmUgZGlmZmVyZW50IGZyb20gb3RoZXJzLCBpdCdzIHVzZWZ1bCB0byBjb25kdWN0IGEgKnBvc3QgaG9jIHRlc3QqLiBBICpUdWtleSBIU0QqIHRlc3QgaXMgY29tbW9ubHkgdXNlZCB0byBjb21wYXJlIGFsbCBncm91cHMgdG8gZWFjaCBvdGhlciAoaS5lLiBhbGwgcG9zc2libGUgY29tcGFyaXNvbnMgb2YgMiBncm91cHMpLCBzcGFyaW5nIHRoZSBuZWVkIHRvIHNldCBhIHJlZmVyZW5jZSBncm91cC4KCmBgYHtyfQpyZXNfdHVrZXkgPC0gVHVrZXlIU0QocmVzX2FvdiwgY29uZi5sZXZlbCA9IDAuOTUpCnJlc190dWtleQpwYXIobWFyID0gYyg1LjEsIDguMSwgNC4xLCAwLjEpKSAjIHNldCBtYXJnaW5zIG9mIHBsb3QKcGxvdChyZXNfdHVrZXksCiAgICAgbGFzID0gMSkgICMgbGFiZWxzIGhvcml6b250YWwgdG8gdGhlIGF4ZXMKYGBgCgpUaGUgcGxvdCBhYm92ZSBkZXBpY3RzIHRoZSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCAoOTUlIENJKSBvZiB0aGUgZGlmZmVyZW5jZXMgaW4gbWVhbiBsZXZlbHMgb2YgY29udGluZW50cy4gQWNjb3JkaW5nIHRvIHRoZSBwbG90LCB0aGUgOTUlIENJIG9mIGBBc2lhLUFtZXJpY2FzYCBhbmQgYE9jZWFuaWEtRXVyb3BlYCBvdmVybGFwcGVkIHdpdGggMCwgbWVhbmluZyB0aGF0IHdlIGNvdWxkbid0IGVmZmVjdGl2ZWx5IHRlbGwgdGhhdCB0aGVzZSBncm91cHMgaGF2ZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBmb3JtZXIgYW5kIHRoZSBsYXR0ZXIgY291bnRyeS4gQWxsIG90aGVyIGdyb3VwcyBoYWQgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMuCgpUaGVzZSBhcmUgdGhlIHN0YXRpc3RpY2FsIHZhbHVlcyBmcm9tIHRoZSAqVHVrZXkgSFNEKiAqdGVzdCo6CgpgYGB7cn0KZGF0YV9mcmFtZShHcm91cCA9IHJvdy5uYW1lcyhyZXNfdHVrZXkkY29udGluZW50KSwgCiAgICAgICAgICAgYXNfZGF0YV9mcmFtZShyZXNfdHVrZXkkY29udGluZW50KSkgJT4lIAogIGFycmFuZ2UoZGVzYyhgcCBhZGpgKSkgJT4lIAogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCA0KSkgJT4lIAogIGRhdGF0YWJsZSgpCmBgYAoKPiBGaW5kaW5nczogVGhlcmUgaXMgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBncm91cCBtZWFucyBvZiBgJ0VuZXJneSB1c2UgKGtnIG9mIG9pbCBlcXVpdmFsZW50IHBlciBjYXBpdGEpJ2AgYWNyb3NzIHRoZSBgY29udGluZW50c2AuIEhvd2V2ZXIsIGEgcGFpcndpc2UgcG9zdC1ob2MgdGVzdCByZXZlYWxzIHRoYXQgaXQncyBub3QgdGhlIGNhc2UgYmV0d2VlbiBgQXNpYS1BbWVyaWNhc2AgYW5kIGBPY2VhbmlhLUV1cm9wZWAuCgojIyBFdXJvcGVhbiBhbmQgQXNpYW4gSW1wb3J0cyBvZiBHb29kcyBhbmQgU2VydmljZXMKCj4gSW5zdHJ1Y3Rpb25zOiBJcyB0aGVyZSBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiBFdXJvcGUgYW5kIEFzaWEgd2l0aCByZXNwZWN0IHRvIGAnSW1wb3J0cyBvZiBnb29kcyBhbmQgc2VydmljZXMgKCUgb2YgR0RQKSdgIGluIHRoZSB5ZWFycyBhZnRlciAxOTkwPyAoc3RhdHMgdGVzdCBuZWVkZWQpCgpXZSBmaWx0ZXJlZCB0aGUgZGF0YXNldCBhY2NvcmRpbmcgdG8gdGhlIGluc3RydWN0aW9ucyBhbmQgZHJldyBhIGJveHBsb3Q6CgpgYGB7cn0KZmlsdGVyZWQuZ2FwbWluZGVyX2NsZWFuIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgZmlsdGVyKGNvbnRpbmVudCAlaW4lIGMoIkV1cm9wZSIsICJBc2lhIiksIFllYXIgPj0gMTk5MCkKYGBgCgpgYGB7cn0KcGxvdCA8LSBmaWx0ZXJlZC5nYXBtaW5kZXJfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeCA9IGNvbnRpbmVudCwgeSA9IGBJbXBvcnRzIG9mIGdvb2RzIGFuZCBzZXJ2aWNlcyAoJSBvZiBHRFApYCwgZmlsbCA9IGNvbnRpbmVudCkpICsKICBnZW9tX2JveHBsb3QobmEucm0gPSBUUlVFKSArIAogIGdndGl0bGUoIkV1cm9wZWFuIGFuZCBBc2lhbiBJbXBvcnRzIG9mIEdvb2RzIGFuZCBTZXJ2aWNlcyIpCmdncGxvdGx5KHBsb3QsIGhlaWdodCA9IDM1MCwgd2lkdGggPSA2MDApCmBgYAoKQXMgY2FuIGJlIHNlZW4gaW4gdGhlIGJveHBsb3QgYW5kIHRoZSBmb2xsb3dpbmcgdGFibGUsIGBBc2lhYCBoYXMgYSBzbGlnaHRseSBoaWdoZXIgbWVhbiBvZiBgSW1wb3J0cyBvZiBnb29kcyBhbmQgc2VydmljZXNgIGNvbXBhcmVkIHRvIGBFdXJvcGVgLgoKYGBge3J9CmZpbHRlcmVkLmdhcG1pbmRlcl9jbGVhbiAlPiUgZ3JvdXBfYnkoY29udGluZW50KSAlPiUgCiAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKGBJbXBvcnRzIG9mIGdvb2RzIGFuZCBzZXJ2aWNlcyAoJSBvZiBHRFApYCwgbmEucm0gPSBUUlVFKSwgCiAgICAgICAgICAgIElRUiA9IElRUihgSW1wb3J0cyBvZiBnb29kcyBhbmQgc2VydmljZXMgKCUgb2YgR0RQKWAsIG5hLnJtID0gVFJVRSkpICU+JSAKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgNCkpICU+JSAKICBkYXRhdGFibGUoKQpgYGAKClNpbmNlIHdlIGFyZSBjb21wYXJpbmcgdHdvIGdyb3VwcyBvZiBxdWFudGl0YXRpdmUgZGF0YSwgYSAqdHdvLXNhbXBsZSB0LXRlc3QqIGNvdWxkIGJlIHVzZWZ1bC4gVGhpcyB0ZXN0IGhvbGRzIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgbWVhbiBvZiBgSW1wb3J0cyBvZiBnb29kcyBhbmQgc2VydmljZXNgIGRvZXMgbm90IGRpZmZlciBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzLgoKYGBge3J9CnJlc190IDwtIHQudGVzdChmb3JtdWxhID0gYEltcG9ydHMgb2YgZ29vZHMgYW5kIHNlcnZpY2VzICglIG9mIEdEUClgIH4gY29udGluZW50LCAKICAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXJlZC5nYXBtaW5kZXJfY2xlYW4pCnJlc190CmBgYAoKPiBGaW5kaW5nczogVGhlIHR3by1zYW1wbGUgdC10ZXN0IGdpdmVzIGEgcC12YWx1ZSBvZiBgciByZXNfdCRwLnZhbHVlYCwgd2hpY2ggaXMgXD4wLjA1LCBtZWFuaW5nIHRoYXQgdGhlIG1lYW4gb2YgYEltcG9ydHMgb2YgZ29vZHMgYW5kIHNlcnZpY2VzYCBpcyBub3Qgc3RhdGlzdGljYWxseSBkaWZmZXJlbnQgYmV0d2VlbiBgRXVyb3BlYCBhbmQgYEFzaWFgLgoKIyMgQ291bnRyaWVzIG9mIEhpZ2hlc3QgUG9wdWxhdGlvbiBEZW5zaXR5Cgo+IEluc3RydWN0aW9uczogV2hhdCBpcyB0aGUgY291bnRyeSAob3IgY291bnRyaWVzKSB0aGF0IGhhcyB0aGUgaGlnaGVzdCBgJ1BvcHVsYXRpb24gZGVuc2l0eSAocGVvcGxlIHBlciBzcS4ga20gb2YgbGFuZCBhcmVhKSdgIGFjcm9zcyBhbGwgeWVhcnM/IChpLmUuLCB3aGljaCBjb3VudHJ5IGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIHJhbmtpbmcgaW4gdGhpcyBjYXRlZ29yeSBhY3Jvc3MgZWFjaCB0aW1lIHBvaW50IGluIHRoZSBkYXRhc2V0PykKClRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uLCB0aGUgbWVhbiBvZiBwb3B1bGF0aW9uIGRlbnNpdHkgb3ZlciB0aGUgeWVhcnMgaXMgY2FsY3VsYXRlZCBmb3IgZWFjaCBjb3VudHJ5LiBJbiB0aGUgZm9sbG93aW5nIHRhYmxlLCB0aGUgY291bnRyaWVzIGFyZSBvcmRlcmVkIGJ5IHRoZSBjYWxjdWxhdGVkIG1lYW4gKHJvdW5kZWQgdG8gdGhlIG5lYXJzZXJ0IGludGVnZXIpLCBpbiBkZXNjZW5kaW5nIG9yZGVyLgoKYGBge3J9CmNvdW50cmllc19vZl9oaWdoZXN0X2RlbnNpdHkgPC0gZ2FwbWluZGVyX2NsZWFuICU+JSAKICBkcGx5cjo6c2VsZWN0KFllYXIsIGBDb3VudHJ5IE5hbWVgLCBgUG9wdWxhdGlvbiBkZW5zaXR5IChwZW9wbGUgcGVyIHNxLiBrbSBvZiBsYW5kIGFyZWEpYCkgJT4lIAogIHNwcmVhZChrZXkgPSBZZWFyLCB2YWx1ZSA9IGBQb3B1bGF0aW9uIGRlbnNpdHkgKHBlb3BsZSBwZXIgc3EuIGttIG9mIGxhbmQgYXJlYSlgKSAlPiUgCiAgYmluZF9jb2xzKC4sIE1lYW4gPSByb3dNZWFucyhkcGx5cjo6c2VsZWN0KC4sIC1gQ291bnRyeSBOYW1lYCkpKSAlPiUgCiAgYXJyYW5nZShkZXNjKE1lYW4pKSAlPiUgCiAgcmVsb2NhdGUoTWVhbiwgLmFmdGVyID0gYENvdW50cnkgTmFtZWApCmNvdW50cmllc19vZl9oaWdoZXN0X2RlbnNpdHkgJT4lIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCAwKSkgJT4lIGRhdGF0YWJsZShvcHRpb25zID0gbGlzdChzY3JvbGxYID0gVFJVRSkpCmBgYAoKRnJvbSB0aGUgdGFibGUgYWJvdmUsIGByIGhlYWQoY291bnRyaWVzX29mX2hpZ2hlc3RfZGVuc2l0eSlbWyJDb3VudHJ5IE5hbWUiXV1gIGhhdmUgdGhlIHRvcCBzaXggaGlnaGVzdCBhdmVyYWdlIHBvcHVsYXRpb24gZGVuc2l0eS4gV2UgY291bGQgZnVydGhlciBkcmF3IGEgbGluZSBwbG90IHRvIG9ic2VydmUgdGhlIGNoYW5nZXMgb2YgcG9wdWxhdGlvbiBkZW5zaXR5IGluIHRoZXNlIGNvdW50cmllcyBvdmVyIHllYXJzLgoKYGBge3J9CnBsb3QgPC0gZ2FwbWluZGVyX2NsZWFuICU+JSAKICBmaWx0ZXIoYENvdW50cnkgTmFtZWAgJWluJSBoZWFkKGNvdW50cmllc19vZl9oaWdoZXN0X2RlbnNpdHkpJGBDb3VudHJ5IE5hbWVgKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IGBQb3B1bGF0aW9uIGRlbnNpdHkgKHBlb3BsZSBwZXIgc3EuIGttIG9mIGxhbmQgYXJlYSlgLCBjb2xvciA9IGBDb3VudHJ5IE5hbWVgKSkgKwogIGdlb21fbGluZShuYS5ybSA9IFRSVUUpICsgCiAgZ2VvbV9wb2ludChuYS5ybSA9IFRSVUUpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIGdndGl0bGUoIkNoYW5nZXMgb2YgUG9wdWxhdGlvbiBEZW5zaXR5IGluIFRvcCBTaXggQ291bnRyaWVzIikKZ2dwbG90bHkocGxvdCwgaGVpZ2h0ID0gMzUwLCB3aWR0aCA9IDYwMCkKYGBgCgo+IEZpbmRpbmdzOiBgciBoZWFkKGNvdW50cmllc19vZl9oaWdoZXN0X2RlbnNpdHksIDIpW1siQ291bnRyeSBOYW1lIl1dYCBhcmUgdGhlIHRvcCAyIGluIGF2ZXJhZ2UgYFBvcHVsYXRpb24gZGVuc2l0eSAocGVvcGxlIHBlciBzcS4ga20gb2YgbGFuZCBhcmVhKWAgYWNyb3NzIGFsbCB5ZWFycy4KCiMjIENvdW50cmllcyBvZiBHcmVhdGVzdCBMaWZlIEV4cGVjdGFuY3kgSW5jcmVhc2UKCj4gSW5zdHJ1Y3Rpb25zOiBXaGF0IGNvdW50cnkgKG9yIGNvdW50cmllcykgaGFzIHNob3duIHRoZSBncmVhdGVzdCBpbmNyZWFzZSBpbiAnTGlmZSBleHBlY3RhbmN5IGF0IGJpcnRoLCB0b3RhbCAoeWVhcnMpJyBiZXR3ZWVuIDE5NjIgYW5kIDIwMDc/CgpUbyBhbnN3ZXIgdGhlIHF1ZXN0aW9uLCB3ZSBmaXJzdCB0cmFuc2Zvcm0gdGhlIHRhYmxlIGludG8gYENvdW50cnkgbmFtZWAgeCBgWWVhcmAsIGFuZCBzdWJ0cmFjdCB0aGUgd2hvbGUgdGFibGUgYnkgdmFsdWUgb2YgYExpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCwgdG90YWwgKHllYXJzKWAgaW4gMTk2Mi4KCmBgYHtyfQpzZWxlY3RlZF9jb2wuZ2FwbWluZGVyX2NsZWFuIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUKICBkcGx5cjo6c2VsZWN0KFllYXIsIGBDb3VudHJ5IE5hbWVgLCBgTGlmZSBleHBlY3RhbmN5IGF0IGJpcnRoLCB0b3RhbCAoeWVhcnMpYCkgJT4lCiAgc3ByZWFkKGtleSA9IGBZZWFyYCwgdmFsdWUgPSBgTGlmZSBleHBlY3RhbmN5IGF0IGJpcnRoLCB0b3RhbCAoeWVhcnMpYCkKCnNlbGVjdGVkX2NvbC5nYXBtaW5kZXJfY2xlYW4gPC0gZGF0YV9mcmFtZSgKICBgQ291bnRyeSBOYW1lYCA9IHNlbGVjdGVkX2NvbC5nYXBtaW5kZXJfY2xlYW4kYENvdW50cnkgTmFtZWAsCiAgZHBseXI6OnNlbGVjdChzZWxlY3RlZF9jb2wuZ2FwbWluZGVyX2NsZWFuLCAtYENvdW50cnkgTmFtZWApIC0gc2VsZWN0ZWRfY29sLmdhcG1pbmRlcl9jbGVhbiRgMTk2MmAgIyBTdWJ0cmFjdCB0aGUgd2hvbGUgdGFibGUgYnkgdmFsdWUgaW4gMTk2MgopICU+JQogIGdhdGhlcigtYENvdW50cnkgTmFtZWAsIGtleSA9IFllYXIsIHZhbHVlID0gYExpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCwgdG90YWwgKHllYXJzKWApCmBgYAoKVGhlIHRhYmxlIGJlbG93IHNob3dzIHRoZSBkaWZmZXJlbmNlIG9mIGBMaWZlIGV4cGVjdGFuY3kgYXQgYmlydGgsIHRvdGFsICh5ZWFycylgIGJldHdlZW4gMjAwNyBhbmQgMTk2MiwgYXJyYW5nZWQgaW4gZGVzY2VuZGluZyBvcmRlciwgb2YgZWFjaCBjb3VudHJ5OgoKYGBge3J9CmNvdW50cmllc19vZl9ncmVhdGVzdF9saWZlX2V4cGVjdGFuY3lfaW5jcmVhc2UgPC0gc2VsZWN0ZWRfY29sLmdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgZHBseXI6OnNlbGVjdChZZWFyLCBgQ291bnRyeSBOYW1lYCwgYExpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCwgdG90YWwgKHllYXJzKWApICU+JSAKICBzcHJlYWQoa2V5ID0gYFllYXJgLCB2YWx1ZSA9IGBMaWZlIGV4cGVjdGFuY3kgYXQgYmlydGgsIHRvdGFsICh5ZWFycylgKSAlPiUgCiAgZHBseXI6OnNlbGVjdChgQ291bnRyeSBOYW1lYCwgYERpZmZlcmVuY2UgYmV0d2VlbiAyMDA3IGFuZCAxOTYyYCA9IGAyMDA3YCkgJT4lIAogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCA0KSkgJT4lIAogIGFycmFuZ2UoZGVzYyhgRGlmZmVyZW5jZSBiZXR3ZWVuIDIwMDcgYW5kIDE5NjJgKSkKY291bnRyaWVzX29mX2dyZWF0ZXN0X2xpZmVfZXhwZWN0YW5jeV9pbmNyZWFzZSAlPiUgZGF0YXRhYmxlKCkKYGBgCgpUaGUgbGluZSBwbG90IHNob3dzIHRoZSB0b3Agc2l4IGNvdW50cmllcyByZWdhcmRpbmcgbGlmZSBleHBlY3RhbmN5IGdyb3d0aC4gWWVhciAxOTYyIG9mIGVhY2ggY291bnRyeSBpcyBzZXQgdG8gYSBiYXNlbGluZSAwIGZvciBiZXR0ZXIgY29tcGFyaXNvbi4KCmBgYHtyfQpwbG90IDwtIHNlbGVjdGVkX2NvbC5nYXBtaW5kZXJfY2xlYW4gJT4lIAogIHN1YnNldChgQ291bnRyeSBOYW1lYCAlaW4lIGhlYWQoY291bnRyaWVzX29mX2dyZWF0ZXN0X2xpZmVfZXhwZWN0YW5jeV9pbmNyZWFzZSkkYENvdW50cnkgTmFtZWApICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gYExpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCwgdG90YWwgKHllYXJzKWAsIGNvbG9yID0gYENvdW50cnkgTmFtZWApKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGBDb3VudHJ5IE5hbWVgKSwgbmEucm0gPSBUUlVFKSArIAogIGdlb21fcG9pbnQobmEucm0gPSBUUlVFKSArIAogIGxhYnMoeSA9ICJEaWZmZXJlbmNlIG9mIGxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCwgdG90YWwgKHllYXJzKSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgZ2d0aXRsZSgiQ2hhbmdlcyBvZiBMaWZlIEV4cGVjdGFuY3kgR3Jvd3RoIGluIFRvcCBTaXggQ291bnRyaWVzIikKZ2dwbG90bHkocGxvdCwgaGVpZ2h0ID0gMzUwLCB3aWR0aCA9IDYwMCkKYGBgCgo+IEZpbmRpbmdzOiBgciBjb3VudHJpZXNfb2ZfZ3JlYXRlc3RfbGlmZV9leHBlY3RhbmN5X2luY3JlYXNlW1sxLCAxXV1gIGhhcyBzaG93biB0aGUgZ3JlYXRlc3QgaW5jcmVhc2UgaW4gYExpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCwgdG90YWwgKHllYXJzKWAgYmV0d2VlbiAxOTYyIGFuZCAyMDA3LgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK